package cn.fx.framework.security;

import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * RSACipherUtil：非对称式加密
 * 1、公钥加密、私钥解密、签名与验签：encrypt()、decrypt()、sign()、checkSign()。
 * 2、生成密钥对：keyPairGenerator()。根据密钥对导出公钥字符串与私钥字符串：getPublicKeyString()、getPrivateKeyString。
 * 3、根据公钥字符串与私钥字符串获取公钥与私钥：getPublicKey()、getPrivateKey()。
 *
 * @author fxcn
 * @since 2024-9-23
 */
public class RSACipherUtil {

    public static final String RSA_ALGORITHM = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";

    /**
     * 生成密钥对(keySize:1024)
     *
     * @return KeyPair 密钥对
     */
    public static KeyPair keyPairGenerator() throws NoSuchAlgorithmException {
        return keyPairGenerator(1024);
    }

    /**
     * 生成密钥对
     * @param keySize 根据密码强度要求，建议输入1024或2048，
     * @return KeyPair 密钥对
     * @throws NoSuchAlgorithmException
     */
    public static KeyPair keyPairGenerator(int keySize) throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(keySize, new SecureRandom()); // 使用51024/2048位密钥,根据密码强度需要
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * 导出公钥字符串
     * @param keyPair 密钥对
     * @return 公钥字符串(Base64编码)
     */
    public static String getPublicKeyString(KeyPair keyPair){
        return Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
    }

    /**
     * 导出私钥字符串
     * @param keyPair 密钥对
     * @return 私钥字符串(Base64编码)
     */
    public static String getPrivateKeyString(KeyPair keyPair){
        return Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
    }

    /**
     * 获取公钥
     *
     * @param key 密钥字符串（经过base64编码）
     * @return 公钥
     */
    public static PublicKey getPublicKey(String key) throws Exception {
        // 按照X.509标准对其进行编码的密钥
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key.getBytes(StandardCharsets.UTF_8)));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        // 生成公钥
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    /**
     * 获取私钥
     *
     * @param key 密钥字符串（经过base64编码）
     * @return 私钥
     */
    public static PrivateKey getPrivateKey(String key) throws Exception {
        // 按照PKCS8格式标准对其进行编码的密钥，首先要将key进行base64解码
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key.getBytes(StandardCharsets.UTF_8)));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        // 生成私钥
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    /**
     * RSA加密
     *
     * @param plainText    原文
     * @param strPublicKey 公钥字符串
     * @return 密文
     * @throws Exception
     */
    public static String encrypt(String plainText, String strPublicKey) throws Exception {
        return encrypt(plainText, getPublicKey(strPublicKey));
    }

    public static String encrypt(String plainText, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));

        byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

    /**
     * RSA解密
     *
     * @param encryptedText 密文
     * @param strPrivateKey 私钥字符串
     * @return 原文
     * @throws Exception
     */
    public static String decrypt(String encryptedText, String strPrivateKey) throws Exception {
        return decrypt(encryptedText, getPrivateKey(strPrivateKey));
    }

    public static String decrypt(String encryptedText, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));

        byte[] decodedBytes = Base64.getDecoder().decode(encryptedText);
        byte[] decryptedBytes = cipher.doFinal(decodedBytes);
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }

    /**
     * 签名
     *
     * @param privateKey 私钥
     * @param content    要进行签名的内容
     * @return 签名
     */
    public static String sign(String privateKey, byte[] content) {
        try {
            // privateKey进行base64编码，然后生成PKCS8格式私钥
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey.getBytes(StandardCharsets.UTF_8)));
            KeyFactory key = KeyFactory.getInstance("RSA");
            PrivateKey priKey = key.generatePrivate(priPKCS8);
            //System.out.println(new String(priKey.getEncoded())); //测试
            // 签名摘要算法
            Signature signature = Signature.getInstance("SHA256WithRSA");
            // 用私钥初始化此对象以进行签名
            signature.initSign(priKey);
            // 使用指定的字节数组更新签名或验证
            signature.update(content);
            // 获得签名字节
            byte[] signed = signature.sign();
            // 进行base64编码返回
            return new String(Base64.getEncoder().encode(signed));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 验签
     *
     * @param publicKey 公钥
     * @param content   要验签的内容
     * @param sign      签名
     * @return 验签结果
     */
    public static boolean checkSign(String publicKey, byte[] content, String sign) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            // 进行base64解码
            byte[] encodedKey = Base64.getDecoder().decode(publicKey);
            // 生成公钥
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            // 签名摘要算法
            Signature signature = Signature.getInstance("SHA256WithRSA");
            // 用公钥初始化签名
            signature.initVerify(pubKey);
            // 使用指定的字节数组更新签名或验证
            signature.update(content);
            // base64解码后进行验证
            return signature.verify(Base64.getDecoder().decode(sign));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public static void main(String[] args) throws Exception {
/*
        //生成公钥私钥
        KeyPair keyPair = keyPairGenerator();
        String publicKeyString = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
        String privateKeyString = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());

        System.out.println("publicKey：" + publicKeyString);
        System.out.println("privateKey：" + privateKeyString);

        // 原始文本
        String originalText = "Hello, RSA Encryption!";
        System.out.println("原文："+originalText);

        // 加密
        String encryptedText = encrypt(originalText, publicKeyString);
        System.out.println("Encrypted Text: " + encryptedText);

        // 解密
        String decryptedText = decrypt(encryptedText, privateKeyString);
        System.out.println("Decrypted Text: " + decryptedText);

        System.out.println("=========================================================");

        //私钥签名
        String signature = sign(privateKeyString, Base64.getDecoder().decode(encryptedText));
        System.out.println("私钥签名：" + signature);

        //公钥验签
        Boolean signResult = checkSign(publicKeyString, Base64.getDecoder().decode(encryptedText), signature);
        System.out.println("验签结果：" + signResult);
*/


    }
}
